package querylayer

import (
	"bytes"
	"database/sql"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/gogo/protobuf/proto"
	_ "github.com/lib/pq" // PostgreSQL 驱动
	"github.com/tendermint/tendermint/my_app/config"
	"github.com/tendermint/tendermint/my_app/querylayer/vindex"
	"github.com/tendermint/tendermint/my_app/utils"
	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)

// QueryEngine 结构体
type QueryEngine struct {
	//pgsql
	db       *sql.DB                  //存储交易数据
	vindexes map[string]vindex.Vindex // “datatype-category” -> Vindex
}

// NewQueryEngine 初始化一个 QueryEngine
func NewQueryEngine() *QueryEngine {
	fmt.Printf("create new QueryEngine\n")
	//连接数据库
	dsn := "user=demo_user password=123456 host=localhost port=5432 dbname=demodb sslmode=disable"
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		panic(err.Error())
	}
	// createUsertable(db)
	//GenerateRandomUserValue(db, config.UserSize)
	// if err := initializeDatabase(db, config.TableSchemaFile); err != nil {
	// 	panic(fmt.Sprintf("failed to initialize database: %v", err))
	// }
	//LoadCovidData(db, 100)
	return &QueryEngine{
		vindexes: make(map[string]vindex.Vindex),
		db:       db,
	}
}

// ShowIndex 返回已建立的索引描述
func (qe *QueryEngine) ShowIndex() []string {
	var indexes []string
	for key := range qe.vindexes {
		indexes = append(indexes, key)
	}
	return indexes
}

// BuildIndex 构建索引
func (qe *QueryEngine) BuildIndex(datatype string, category string) error {
	// 检查是否已经存在索引
	indexname := qe.indexname(datatype, category)
	if _, exists := qe.vindexes[indexname]; exists {
		return errors.New("index already exists for datatype and category")

	}

	// 根据不同的 datatype 创建相应的 Vindex
	var vidx vindex.Vindex
	switch datatype {
	case "image":
		vidx = &vindex.ImageVindex{}
		vidx.BuildIndex(config.Image_Dimension)
	case "series":
		vidx = &vindex.SeriesVindex{}
		vidx.BuildIndex(config.Series_Dimension)
	case "text":
		vidx = &vindex.TextVindex{}
		vidx.BuildIndex(config.Text_Dimension)
	default:
		return errors.New("unknown datatype")

	}

	// 注册新的 Vindex
	qe.registerVindex(datatype, category, vidx)

	// 查询文件数据并插入到索引中
	query := `
		SELECT object_id, eigenvalue 
		FROM file_data 
		WHERE file_type = $1 AND file_category = $2
	`
	rows, err := qe.db.Query(query, datatype, category)

	if err != nil {
		return fmt.Errorf("failed to query file_data table: %v", err)
	}
	defer rows.Close()

	// 处理每一行数据
	for rows.Next() {
		var objectId string
		var eigenvaluebyte []byte
		if err := rows.Scan(&objectId, &eigenvaluebyte); err != nil {
			return fmt.Errorf("failed to scan row: %v", err)
		}
		eigenvalue, err := utils.BinaryToFloat32(eigenvaluebyte)
		if err != nil {
			return fmt.Errorf("failed to insert data into vindex: %v", err)
		}
		// 调用 vidx.InsertData 方法
		if err := vidx.InsertData(objectId, eigenvalue); err != nil {
			return fmt.Errorf("failed to insert data into vindex: %v", err)
		}
	}

	fmt.Printf("build index successfully\n")

	return nil

}

// InsertData 插入数据 key=DATA-datatype-category-objectid-organization-ownerid
func (qe *QueryEngine) InsertData(datatype string, category string, object_id string, data interface{}) error {

	idxname := qe.indexname(datatype, category)
	if vindex, exists := qe.vindexes[idxname]; exists {
		//返回索引内的id
		log.Printf("insert into %s index\n", idxname)
		err := vindex.InsertData(object_id, data)
		return err
	} else {
		// 未建立索引
		// fmt.Printf("no %s index\n", idxname)
		return nil
	}
}

// DeleteData 删除数据
// 或修改为key=DATA-datatype-category-objectid-organization-ownerid TODO 待确认
func (qe *QueryEngine) DeleteData(datatype string, category string, object_id string) error {

	idxname := qe.indexname(datatype, category)
	if vindex, exists := qe.vindexes[idxname]; exists {
		//返回索引内的id
		err := vindex.DeleteData(object_id)
		return err
	} else {
		//未建立索引
		return nil
	}
}

// KnnQuery 执行 KNN 查询
func (qe *QueryEngine) KnnQuery(datatype string, category string, data interface{},
	k int32, ef int32) ([]string, [][]float32, []byte) {
	idxname := qe.indexname(datatype, category)
	if vindex, exists := qe.vindexes[idxname]; exists {
		ids, vectors, proof := vindex.KnnQuery(data, int(k), int(ef))
		return ids, vectors, proof
	} else {
		fmt.Printf("Vindex not found for datatype: %s, category: %s\n", datatype, category)
		return nil, nil, nil
	}

}

// RegisterVindex 注册一个 Vindex
func (qe *QueryEngine) registerVindex(datatype string, category string, v vindex.Vindex) {
	idxname := qe.indexname(datatype, category)
	if _, exists := qe.vindexes[idxname]; !exists {
		qe.vindexes[idxname] = v
	}

	//qe.storeIndex(datatype, category)
}

// key=DATA-datatype-category-objectid-organization-ownerid
func GenerateDataKey(data_type, category, object_id, organization, ownerID string) string {
	return fmt.Sprintf("DATA-%s-%s-%s-%s-%s", data_type, category, object_id, organization, ownerID)
}

func (qe *QueryEngine) indexname(datatype string, category string) string {
	return datatype + "-" + category
}
func (qe *QueryEngine) processfiledata(tx *tmproto.Tx) error {
	switch tx.OperationType {

	case tmproto.OperationType_PUT:
		data := new(tmproto.FileData)
		if err := proto.Unmarshal(tx.Data, data); err != nil {
			panic(err)
		}

		// 将数据存入数据库
		err := qe.insertFileData(data)
		if err != nil {
			return err
		}
		//查询层维护索引
		err = qe.InsertData(data.FileType, data.FileCategory.String(), data.ObjectId, data.Eigenvalue)
		if err != nil {
			return err
		}

	case tmproto.OperationType_DELETE:
		data := new(tmproto.FileData)
		if err := proto.Unmarshal(tx.Data, data); err != nil {
			panic(err)
		}

		// 将数据从数据库删除
		err := qe.deleteFileData(data)
		if err != nil {
			fmt.Println("fail to delete data from db")
			return err
		}

		//调用查询层维护索引
		err = qe.DeleteData(data.FileType, data.FileCategory.String(), data.ObjectId)
		if err != nil {
			fmt.Println("fail to insert into index")
			return err
		}

	}

	return nil
}

func (qe *QueryEngine) DeliverTx(tx *tmproto.Tx) error {

	data := new(tmproto.FileData)
	if err := proto.Unmarshal(tx.Data, data); err != nil {
		panic(err)
	}
	return qe.processfiledata(tx)
}

func (qe *QueryEngine) Sql_Query(sql string) []string {
	fmt.Println("TODO")
	// 写入文件内容
	// content := "select l_orderkey,o_orderdate,o_shippriority from customer join orders on c_custkey = o_custkey join lineitem on o_orderkey = l_orderkey where c_mktsegment = 'AUTOMOBILE' and o_orderdate > date '1998-08-01' and l_shipdate > date '1998-11-28' order by o_orderdate; "

	fileName := "params.txt"
	err := os.WriteFile(fileName, []byte(sql), 0777)
	if err != nil {
		fmt.Println("Error writing to file:", err)
		return nil
	}

	// 打开文件并发送 HTTP POST 请求
	file, err := os.Open(fileName)
	if err != nil {
		fmt.Println("Error opening file:", err)
		return nil
	}
	defer file.Close()

	// 创建 HTTP 请求
	action := 1 // 处理方法选择
	url := fmt.Sprintf("http://10.77.110.184:8088?action=%d", action)
	body := &bytes.Buffer{}
	_, err = io.Copy(body, file)
	if err != nil {
		fmt.Println("Error copying file content:", err)
		return nil
	}

	resp, err := http.Post(url, "application/octet-stream", body)
	if err != nil {
		fmt.Println("Error sending POST request:", err)
		return nil
	}
	defer resp.Body.Close()

	// 保存服务端返回的结果文件
	resultFile := "result.txt"
	output, err := os.Create(resultFile)
	if err != nil {
		fmt.Println("Error creating result file:", err)
		return nil
	}
	defer output.Close()

	_, err = io.Copy(output, resp.Body)
	if err != nil {
		fmt.Println("Error saving result to file:", err)
		return nil
	}

	// 读取并解析结果文件内容为字符串数组
	result, err := os.ReadFile(resultFile)
	if err != nil {
		fmt.Println("Error reading result file:", err)
		return nil
	}

	lines := strings.Split(string(result), "\n")
	fmt.Println("Result array from server:")
	for i, line := range lines {
		if line != "" {
			fmt.Printf("[%d]: %s\n", i, line)
		}
	}
	return lines
}
